package cn.sxgan.gen.services.core;// CodeGenerator.java

import cn.sxgan.gen.enums.DataSourceType;
import cn.sxgan.gen.enums.DatePatternEnum;
import cn.sxgan.gen.model.ColumnInfo;
import cn.sxgan.gen.model.DbTable;
import cn.sxgan.gen.model.GenCodeBaseInfo;
import cn.sxgan.gen.services.base.DataSourceQuery;
import cn.sxgan.gen.services.base.DataSourceQueryFactory;
import cn.sxgan.gen.utils.CommonUtils;
import cn.sxgan.gen.utils.DateUtils;
import cn.sxgan.gen.utils.FileUtils;
import cn.sxgan.gen.utils.JdbcTypeConverterUtils;
import cn.sxgan.gen.utils.ZipUtil;
import freemarker.template.Configuration;
import freemarker.template.Template;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.FileWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.UUID;

@Slf4j
@Service
public class CodeGeneratorService {
    
    @Autowired
    DataSourceQueryFactory dataSourceQueryFactory;
    
    @Autowired
    private Configuration freemarkerConfig;
    
    @Value("${generator.output-dir}")
    private String outputDir;
    
    @Value("${generator.output-zip}")
    private String outputZipPath;
    
    // 主生成方法
    public void generateSingleTable(HttpServletResponse response, DbTable dbTable) throws Exception {
        
        String uuid = UUID.randomUUID().toString();
        String randomDir = uuid.replaceAll("-", "").toUpperCase();
        outputDir = outputDir + "/" + randomDir;
        // 创建输出目录
        new File(outputDir).mkdirs();
        generateCode(Collections.singletonList(dbTable));
        outputZipPath = outputZipPath + "/" + randomDir;
        String zipFilePath = outputZipPath + "/" + dbTable.getTableName() + ".zip";
        ZipUtil.compressToZip(outputDir, zipFilePath);
        FileUtils.writeFileToResponse(response, zipFilePath, true);
        log.info("当前压缩文件完成，路径：{}", zipFilePath);
        FileUtils.deleteFileOrDirectory(new File(outputDir));
        FileUtils.deleteFileOrDirectory(new File(outputZipPath));
    }
    
    /**
     * 生成多个表
     *
     * @param response
     * @param dbTables
     */
    public void generateMultiTable(HttpServletResponse response, List<DbTable> dbTables) throws Exception {
        String uuid = UUID.randomUUID().toString();
        String randomDir = uuid.replaceAll("-", "").toUpperCase();
        outputDir = outputDir + "/" + randomDir;
        // 创建输出目录
        new File(outputDir).mkdirs();
        generateCode(dbTables);
        outputZipPath = outputZipPath + "/" + randomDir;
        String zipFilePath = outputZipPath + "/GenerateCode.zip";
        ZipUtil.compressToZip(outputDir, zipFilePath);
        FileUtils.writeFileToResponse(response, zipFilePath, true);
        log.info("当前多表生产文件压缩完成，路径：{}", zipFilePath);
        FileUtils.deleteFileOrDirectory(new File(outputDir));
        FileUtils.deleteFileOrDirectory(new File(outputZipPath));
    }
    
    private void generateCode(List<DbTable> datas) throws Exception {
        List<GenCodeBaseInfo> genCodeBaseInfoList = prepareTemplateData(datas);
        for (GenCodeBaseInfo genCodeBaseInfo : genCodeBaseInfoList) {
            log.info("开始生成代码，当前表：{}", genCodeBaseInfo.getTableName());
            // 生成Model
            generateFile("entity.ftl",
                    genCodeBaseInfo,
                    outputDir + "/model/" + genCodeBaseInfo.getClassName() + ".java");
            // 生成VO
            generateFile("vo.ftl",
                    genCodeBaseInfo,
                    outputDir + "/model/vo/" + genCodeBaseInfo.getClassName() + ".java");
            // 生成query
            generateFile("query.ftl",
                    genCodeBaseInfo,
                    outputDir + "/model/query/" + genCodeBaseInfo.getClassName() + ".java");
            // 生成convert
            generateFile("convert.ftl",
                    genCodeBaseInfo,
                    outputDir + "/model/convert/" + genCodeBaseInfo.getClassName() + ".java");
            
            // 生成Mapper
            generateFile("mapper.ftl",
                    genCodeBaseInfo,
                    outputDir + "/mapper/" + genCodeBaseInfo.getClassName() + "Mapper.java");
            
            // 生成MapperXML
            generateFile("mapperxml.ftl",
                    genCodeBaseInfo,
                    outputDir + "/mapper/xml/" + genCodeBaseInfo.getClassName() + "Mapper.xml");
            
            // 生成Service
            generateFile("service.ftl",
                    genCodeBaseInfo,
                    outputDir + "/service/" + genCodeBaseInfo.getClassName() + "Service.java");
            
            // 生成ServiceImpl
            generateFile("serviceImpl.ftl",
                    genCodeBaseInfo,
                    outputDir + "/service/impl/" + genCodeBaseInfo.getClassName() + "ServiceImpl.java");
            log.info("当前表：{}生成完毕！", genCodeBaseInfo.getTableName());
        }
        
    }
    
    private void generateFile(String templateName,
                              GenCodeBaseInfo data,
                              String outputPath) throws Exception {
        Template template = freemarkerConfig.getTemplate(templateName);
        File file = new File(outputPath);
        new File(file.getParent()).mkdirs();
        file.createNewFile();
        try (Writer out = new FileWriter(outputPath)) {
            template.process(data, out);
            log.info("文件生成成功: {}", outputPath);
        }
    }
    
    /**
     * 传入表集合，返回模板数据
     *
     * @param dbTableList
     * @return
     */
    private List<GenCodeBaseInfo> prepareTemplateData(List<DbTable> dbTableList) {
        List<GenCodeBaseInfo> genCodeBaseInfoList = new ArrayList<>();
        try {
            for (DbTable dbTable : dbTableList) {
                GenCodeBaseInfo genCodeBaseInfo = getGenCodeBaseInfo(dbTable);
                genCodeBaseInfoList.add(genCodeBaseInfo);
            }
            return genCodeBaseInfoList;
        } catch (Exception e) {
            e.printStackTrace();
            log.error("异常", e);
            return Collections.emptyList();
        }
    }
    
    public GenCodeBaseInfo getGenCodeBaseInfo(DbTable dbTable) {
        GenCodeBaseInfo genCodeBaseInfo = new GenCodeBaseInfo();
        String tableName = dbTable.getTableName();
        // 表名转类名（下划线转驼峰）
        String className = CommonUtils.convertToCamelCase(tableName, true);
        String varName = CommonUtils.convertToCamelCase(tableName, false);
        List<ColumnInfo> tableColumns = dbTable.getColumnInfos();
        if (tableColumns == null) {
            DataSourceQuery dataSourceQuery = dataSourceQueryFactory.getDataSourceQuery(DataSourceType.MySQL);
            List<DbTable> dbTables = dataSourceQuery.selectDbTables(tableName);
            dbTable = dbTables.getFirst();
        }
        // 模拟表字段（实际应从数据库读取）
        List<ColumnInfo> columns = new ArrayList<>();
        for (ColumnInfo tableColumn : tableColumns) {
            ColumnInfo columnInfo = columnInfo(tableColumn.getName(), tableColumn.getType(), tableColumn.getComment());
            columns.add(columnInfo);
        }
        genCodeBaseInfo.setTableName(tableName);
        genCodeBaseInfo.setTableComment(dbTable.getTableComment());
        genCodeBaseInfo.setClassName(className);
        genCodeBaseInfo.setVarName(varName);
        genCodeBaseInfo.setColumns(columns);
        genCodeBaseInfo.setPackageName("com.example");
        genCodeBaseInfo.setAuthor("sxgan");
        genCodeBaseInfo.setDateTime(DateUtils.getTimeString(DatePatternEnum.YYYY_MM_DD_HH_MM_SS));
        genCodeBaseInfo.setVersion("1.0.0");
        return genCodeBaseInfo;
    }
    
    
    private ColumnInfo columnInfo(String name, String type, String comment) {
        ColumnInfo col = new ColumnInfo();
        col.setName(name);
        col.setType(type);
        col.setJavaName(CommonUtils.convertToCamelCase(name, false));
        col.setJavaType(JdbcTypeConverterUtils.convertToJavaType(type));
        col.setComment(comment);
        return col;
    }
    
    
}